﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace iNet.Media.Imaging.Filters
{
	/// <summary>
	/// 提供色彩轉換表的建立與合併相關功能。
	/// </summary>
	public static class ColorTransformTableFactory
	{
		#region CombineTables
		/// <summary>
		/// 合併兩個轉換表。
		/// </summary>
		/// <param name="table1">第一個轉換表。</param>
		/// <param name="table2">第二個轉換表。</param>
		/// <returns>合併後的轉換表。</returns>
		/// <exception cref="ArgumentException">table1 及 table2 皆為空陣列 -或- table1 或 table2 的長度不相同。</exception>
		/// <exception cref="ArgumentNullException">table1 及 table2 皆為 Null (Visual Basic 為 Nothing)。</exception>
		public static int[] CombineTables(int[] table1, int[] table2)
		{
			//確認參數
			if (table1 == null && table2 == null)
				throw new ArgumentNullException();

			//建立新的轉換表
			int[] resultTable = new int[table1 != null ? table1.Length : table2.Length];
			CombineTables(table1, table2, resultTable);
			return resultTable;
		}
		/// <summary>
		/// 合併兩個轉換表。
		/// </summary>
		/// <param name="table1">第一個轉換表。</param>
		/// <param name="table2">第二個轉換表。</param>
		/// <param name="resultTable">存放合併結果的轉換表。</param>
		/// <exception cref="ArgumentException">resultTable 的長度為零 -或- table1 及 table2 皆為空陣列 -或- table1 或 table2 的長度與 resultTable 長度不相同。</exception>
		/// <exception cref="ArgumentNullException">resultTable 為 Null (Visual Basic 為 Nothing)。</exception>
		public static void CombineTables(int[] table1, int[] table2, int[] resultTable)
		{
			//確認色彩個數
			if (resultTable == null)
				throw new ArgumentNullException("resultTable");
			int colorCount = resultTable.Length;
			if (colorCount == 0)
				throw new ArgumentException();

			//確認參數
			if (table1 == null || table1.Length == 0 || table1 == table2)
			{
				if (resultTable == table2)
					throw new ArgumentException();
				if (table2 == null || table2.Length != colorCount)
					throw new ArgumentException();
				Array.Copy(table2, resultTable, colorCount);
				return;
			}
			else if (table1.Length != colorCount)
				throw new ArgumentException();
			if (table2 == null || table2.Length == 0)
			{
				if (resultTable != table1)
					Array.Copy(table1, resultTable, colorCount);
				return;
			}
			else if (table2.Length != colorCount)
				throw new ArgumentException();

			//合併轉換表
			int maxColor = (colorCount - 1);
			for (int i = colorCount - 1; i >= 0; --i)
				resultTable[i] = table2[Math.Min(Math.Max(0, table1[i]), maxColor)];
		}
		#endregion
		#region CreateGammaContrastTransform
		/// <summary>
		/// 建立基於 Gamma 轉換的對比轉換表。
		/// </summary>
		/// <param name="colorCount">色彩數量。</param>
		/// <param name="contrast">對比值，範圍為 -1 ~ 1。0 表示最低對比；1 表示最高對比；0 表示原始對比。</param>
		/// <returns>對比轉換表。</returns>
		/// <exception cref="ArgumentOutOfRangeException">colorCount 為負值 -或- contrast 超出範圍。</exception>
		public static int[] CreateGammaContrastTransform(int colorCount, double contrast)
		{
			//確認參數
			if (colorCount < 0)
				throw new ArgumentOutOfRangeException("colorCount");
			
			//建立轉換表
			int[] table = new int[colorCount];
			CreateGammaContrastTransform(table, contrast);
			return table;
		}
		/// <summary>
		/// 建立基於 Gamma 轉換的對比轉換表。
		/// </summary>
		/// <param name="resultTable">存放結果的轉換表。</param>
		/// <param name="contrast">對比值，範圍為 -1 ~ 1。0 表示最低對比；1 表示最高對比；0 表示原始對比。</param>
		/// <exception cref="ArgumentNullException">resultTable 為 Null (Visual Basic 為 Nothing)。</exception>
		/// <exception cref="ArgumentOutOfRangeException">contrast 超出範圍。</exception>
		public static void CreateGammaContrastTransform(int[] resultTable, double contrast)
		{
			//確認參數
			if (resultTable == null)
				throw new ArgumentNullException("resultTable");
			if (contrast < -1 || contrast > 1)
				throw new ArgumentOutOfRangeException("contrast");
			
			//建立轉換表
			int colorCount = resultTable.Length;
			int maxColor = (colorCount - 1);
			if (Math.Abs(contrast) < 0.00001)
			{
				for (int i = colorCount - 1; i >= 0; --i)
					resultTable[i] = i;
			}
			else if (contrast <= -0.99999)
			{
				int color = (colorCount / 2);
				for (int i = colorCount - 1; i >= 0; --i)
					resultTable[i] = color;
			}
			else if (contrast >= 0.99999)
			{
				int threshold = (colorCount / 2);
				for (int i = colorCount - 1; i > threshold; --i)
					resultTable[i] = maxColor;
			}
			else
			{
				double gamma = (contrast > 0 ? (1 - contrast) : (1 / (contrast + 1)));
				int threshold = (colorCount / 2);
				for (int i = 0; i < threshold; ++i)
				{
					double value = Math.Pow((double)i / (threshold - 1), gamma);
					resultTable[threshold + i] = Math.Min(maxColor, (threshold + (int)(value * threshold)));
					resultTable[threshold - i - 1] = (int)((1 - value) * threshold);
				}
			}
		}
		#endregion
		#region CreateLinearBrightnessTransform
		/// <summary>
		/// 建立線性亮度轉換表。
		/// </summary>
		/// <param name="colorCount">色彩數量。</param>
		/// <param name="brightness">亮度調整值，範圍為 -1 ~ 1。-1 表示最暗；1 表示最亮；0 表示原始亮度。</param>
		/// <returns>線性亮度轉換表。</returns>
		/// <exception cref="ArgumentOutOfRangeException">colorCount 為負值 -或- brightness 超出範圍。</exception>
		public static int[] CreateLinearBrightnessTransform(int colorCount, double brightness)
		{
			//確認參數
			if (colorCount < 0)
				throw new ArgumentOutOfRangeException("colorCount");

			//建立轉換表
			int[] table = new int[colorCount];
			CreateLinearBrightnessTransform(table, brightness);
			return table;
		}
		/// <summary>
		/// 建立線性亮度轉換表。
		/// </summary>
		/// <param name="resultTable">存放結果的轉換表。</param>
		/// <param name="brightness">亮度調整值，範圍為 -1 ~ 1。-1 表示最暗；1 表示最亮；0 表示原始亮度。</param>
		/// <exception cref="ArgumentNullException">resultTable 為 Null (Visual Basic 為 Nothing)。</exception>
		/// <exception cref="ArgumentOutOfRangeException">brightness 超出範圍。</exception>
		public static void CreateLinearBrightnessTransform(int[] resultTable, double brightness)
		{
			//確認參數
			if (resultTable == null)
				throw new ArgumentNullException("resultTable");
			if (brightness < -1 || brightness > 1)
				throw new ArgumentOutOfRangeException("contrast");

			//建立轉換表
			int colorCount = resultTable.Length;
			int maxColor = (colorCount - 1);
			int colorDiff = (int)(brightness * maxColor + 0.0001);
			for (int i = colorCount - 1; i >= 0; --i)
				resultTable[i] = (i + colorDiff);
		}
		#endregion
		#region CreateLinearContrastTransform
		/// <summary>
		/// 建立線性對比轉換表。
		/// </summary>
		/// <param name="colorCount">色彩數量。</param>
		/// <param name="contrast">對比值，範圍為 -1 ~ 1。0 表示最低對比；1 表示最高對比；0 表示原始對比。</param>
		/// <returns>線性對比轉換表。</returns>
		/// <exception cref="ArgumentOutOfRangeException">colorCount 為負值 -或- contrast 超出範圍。</exception>
		public static int[] CreateLinearContrastTransform(int colorCount, double contrast)
		{
			//確認參數
			if (colorCount < 0)
				throw new ArgumentOutOfRangeException("colorCount");

			//建立轉換表
			int[] table = new int[colorCount];
			CreateLinearContrastTransform(table, contrast);
			return table;
		}
		/// <summary>
		/// 建立線性對比轉換表。
		/// </summary>
		/// <param name="resultTable">存放結果的轉換表。</param>
		/// <param name="contrast">對比值，範圍為 -1 ~ 1。0 表示最低對比；1 表示最高對比；0 表示原始對比。</param>
		/// <exception cref="ArgumentNullException">resultTable 為 Null (Visual Basic 為 Nothing)。</exception>
		/// <exception cref="ArgumentOutOfRangeException">contrast 超出範圍。</exception>
		public static void CreateLinearContrastTransform(int[] resultTable, double contrast)
		{
			//確認參數
			if (resultTable == null)
				throw new ArgumentNullException("resultTable");
			if (contrast < -1 || contrast > 1)
				throw new ArgumentOutOfRangeException("contrast");

			//建立轉換表
			int colorCount = resultTable.Length;
			int middleColor = (colorCount / 2);
			double stretch = (contrast >= 0 ? (1 + (contrast * 2)) : (1 / (1 + (contrast * -2))));
			int shift = (middleColor - (int)(middleColor * stretch));
			CreateLinearTransform(resultTable, stretch, shift);
		}
		#endregion
		#region CreateLinearTransform
		/// <summary>
		/// 建立線性轉換表。
		/// </summary>
		/// <param name="colorCount">色彩數量。</param>
		/// <param name="stretch">倍數係數。</param>
		/// <param name="shift">位移係數。</param>
		/// <returns>線性轉換表。</returns>
		/// <exception cref="ArgumentOutOfRangeException">contrast 為負數。</exception>
		public static int[] CreateLinearTransform(int colorCount, double stretch, int shift)
		{
			//確認參數
			if (colorCount < 0)
				throw new ArgumentOutOfRangeException("colorCount");

			//建立轉換表
			int[] table = new int[colorCount];
			CreateLinearTransform(table, stretch, shift);
			return table;
		}
		/// <summary>
		/// 建立線性轉換表。
		/// </summary>
		/// <param name="resultTable">存放結果的轉換表。</param>
		/// <param name="stretch">倍數係數。</param>
		/// <param name="shift">位移係數。</param>
		/// <exception cref="ArgumentNullException">resultTable 為 Null (Visual Basic 為 Nothing)。</exception>
		public static void CreateLinearTransform(int[] resultTable, double stretch, int shift)
		{
			//確認參數
			if (resultTable == null)
				throw new ArgumentNullException("resultTable");

			//計算轉換表
			for (int i = resultTable.Length - 1; i >= 0; --i)
				resultTable[i] = (int)(i * stretch + shift);
		}
		#endregion
	}
}
